#include "Analyzer.h"
#include <algorithm>
#include <iostream> //for debug

using namespace std;

vector<string> Analyzer::types;
vector<string> Analyzer::keywords1;
vector<string> Analyzer::keywords2;
vector<string> Analyzer::operators;
vector<string> Analyzer::delimiters;


// error messages
const string PARSER_NOT_READY_ERR_MSG = "parser not initialized right";

const string ROUND_BRACE_EOF_ERR_MSG = "missing a )";
const string ROUND_BRACE_CHECK_ERR_MSG = "\")\" without \"(\"";

const string CURLY_BRACE_EOF_ERR_MSG = "missing a }";
const string CURLY_BRACE_CHECK_ERR_MSG = "\"}\" without \"{\"";

const string SQUARE_BRACE_EOF_ERR_MSG = "missing a ]";
const string SQUARE_BRACE_CHECK_ERR_MSG = "\"]\" without \"[\"";

const string ELSE_ERROR_MSG = "\"else\" without \"if\"";
//end of error messages

Analyzer::Analyzer(){
	curlyBraces=0; // { }
	roundBraces=0; // ()
	squareBraces=0; // []
	ifcounter=0;
	mainCounter=0;
	typeFlag = false;
	typeValDecl = "";
}
Analyzer::~Analyzer(){}

string Analyzer::analyze(Parser& p){
	stringstream errStream;
	string output;

	if (!p.isParserReady()){
		return PARSER_NOT_READY_ERR_MSG;
	}

	Token token;
	string errMsg = "";

	p.getToken(token);
	while ((!token.BadToken()) && (!token.eofToken())){
		
		errMsg = mainCheck(token);
		errStream << errMsg;

		errMsg = bracesCheck(token);
		errStream << errMsg;

		errMsg = ifCheck(token);
		errStream << errMsg;

		errMsg = typeDclCheck(token);
		errStream << errMsg;

		p.getToken(token);
	}

	// parse an output string of symbols
	stringstream symbols;
	
	// add variables
	map<string, string>::iterator iter;
	for (iter = symbolTable.begin(); iter != symbolTable.end(); iter++){
		symbols << (*iter).second << "\t" << (*iter).first << endl;
	}

	if (token.BadToken()){
		errStream << "a reading error occured during parsing at line " << token.getLine() << "\n";
	}
	output = errStream.str() +"\n"+ symbols.str();
	return output;
}

void Analyzer::setStaticArrays(){
	types.push_back("char");
	types.push_back("short");
	types.push_back("int");
	types.push_back("long");
	types.push_back("float");
	types.push_back("double");
	types.push_back("void");
	types.push_back("function");

	keywords1.push_back("if");
	keywords1.push_back("else");
	keywords1.push_back("for");
	keywords1.push_back("while");

	keywords2.push_back("class");
	keywords2.push_back("private");
	keywords2.push_back("public");
	keywords2.push_back("const");
	keywords2.push_back("virtual");

	operators.push_back("++");
	operators.push_back("--");
	operators.push_back("==");
	operators.push_back("->");
	operators.push_back("=");
	operators.push_back("+");
	operators.push_back("-");
	operators.push_back("*");
	operators.push_back("&");
	operators.push_back("<<");
	operators.push_back(">>");

	delimiters.push_back("(");
	delimiters.push_back(")");
	delimiters.push_back("[");
	delimiters.push_back("]");
	delimiters.push_back("{");
	delimiters.push_back("}");
	delimiters.push_back(";");
	delimiters.push_back("<");
	delimiters.push_back(">");
	delimiters.push_back("=");
	delimiters.push_back("+");
	delimiters.push_back("-");
	delimiters.push_back("*");
	delimiters.push_back("&");
}

string Analyzer::bracesCheck(const Token& t){
	if (t.BadToken()){
		return "";
	}

	stringstream msg;
	const string& val = t.getValue();
	if (t.eofToken()){
		if (roundBraces > 0){
			msg << "Line " << t.getLine() << ": error - " << ROUND_BRACE_EOF_ERR_MSG << endl;
		}
		if (curlyBraces > 0){
			msg << "Line " << t.getLine() << ": error - " << CURLY_BRACE_EOF_ERR_MSG << endl;
		}
		if (squareBraces > 0){
			msg << "Line " << t.getLine() << ": error - " << SQUARE_BRACE_EOF_ERR_MSG << endl;
		}
	}
	else {
		if (val.compare("(") == 0){
			roundBraces++;
		}
		else if (val.compare(")") == 0){
			roundBraces--;
			if (roundBraces < 0) { 
				roundBraces = 0; //fix the error localy to do more checks
				msg << "Line " << t.getLine() << ": error - "  << ROUND_BRACE_CHECK_ERR_MSG << endl;
			}
		}
		else if (val.compare("[") == 0){
			squareBraces++;
		}
		else if (val.compare("]") == 0){
			squareBraces--;
			if (squareBraces < 0) { 
				squareBraces = 0; //fix the error localy to do more checks
				msg << "Line " << t.getLine() << ": error - " << SQUARE_BRACE_CHECK_ERR_MSG << endl;
			}
		}
		else if (val.compare("{") == 0){
			curlyBraces++;
		}
		else if (val.compare("}") == 0){
			curlyBraces--;
			if (curlyBraces < 0) { 
				curlyBraces = 0; //fix the error localy to do more checks
				msg << "line " << t.getLine() << ": error - " << CURLY_BRACE_CHECK_ERR_MSG << endl;
			}
		}
	}// end of outter if

	return msg.str();	
}

bool Analyzer::isPredefined(const Token& t){
	const string& val = t.getValue();
	vector<string>::iterator it;
	
	it = find(types.begin(), types.end(), val);
	if (it != types.end()){
		return true;
	}

	it = find(keywords1.begin(), keywords1.end(), val);
	if (it != keywords1.end()){
		return true;
	}

	it = find(keywords2.begin(), keywords2.end(), val);
	if (it != keywords2.end()){
		return true;
	}

	it = find(operators.begin(), operators.end(), val);
	if (it != operators.end()){
		return true;
	}

	it = find(delimiters.begin(), delimiters.end(), val);
	if (it != delimiters.end()){
		return true;
	}

	return false;
}


bool Analyzer::isIdentifier(const Token& t){

	char c = t.getValue().at(0);
	if ((c >= '0') && (c <= '9')){
		return false;
	}
	else if (isPredefined(t)){
		return false;
	}

	return true;
}


string Analyzer::ifCheck(const Token& t){
	if (t.BadToken()){
		return "";
	}
	if (t.eofToken()){
		return "";
	}

	stringstream msg;
	const string& val = t.getValue();
	if (val.compare("if") == 0){
		ifcounter++;
	}
	else if (val.compare("else") == 0){
		ifcounter--;
		if (ifcounter < 0){
			ifcounter = 0;
			msg << "line " << t.getLine() << ": error - " << ELSE_ERROR_MSG << endl;
		}
	}

	return msg.str();
}

string Analyzer::typeDclCheck(const Token& t){

	if (t.BadToken()){
		return "";
	}

	stringstream msg;
	// already seen a type
	if (typeFlag){
		//check if the token is an ID
		if (isIdentifier(t)){
			//check if it was declared
			map<string,string>::iterator it = symbolTable.find(t.getValue());
			if (it == symbolTable.end()){
				// add the id and type to the map and reset the type flag
				symbolTable.insert(pair<string, string>(t.getValue(), typeValDecl));
				typeFlag = false;
				typeValDecl = "";
			}
			else{
				typeFlag = false;
				typeValDecl = "";
				msg << "line " << t.getLine() << ": error - " << t.getValue() << " already declared" << endl;
			}
		}
		else {
			if (isPredefined(t)){
				vector<string>::iterator it = find(types.begin(), types.end(), t.getValue());
				if (it != types.end()){
					// [type] [type] is illegal
					typeValDecl = t.getValue();
					msg << "line " << t.getLine() << ":  error - multiple type definition" << endl;
				}
				else {
					msg << "line " << t.getLine() << ":  error - only identifier allowed after predefined type" << endl;
					typeFlag = false;
					typeValDecl = "";
				}
			}
		}
	}
	else {	// previous token was not a 'type' 
		// check if this token is a predefined type
		vector<string>::iterator it = find(types.begin(), types.end(), t.getValue());
		if (it != types.end()){

			// raise the flag for next token and store the type seen
			typeFlag = true;
			typeValDecl = t.getValue();
		}
		else if (isIdentifier(t)){
			// check if the identifier was previously declared
			map<string,string>::iterator it = symbolTable.find(t.getValue());
			if (it == symbolTable.end()){
				msg << "line " << t.getLine() << ": error - " << t.getValue() << " was not declared" << endl;
			}
		}
	}

	return msg.str();
}


string Analyzer::mainCheck(const Token& t){
	if (mainCounter > 1){
		return "";
	}

	string msg;
	const string& val = t.getValue();
	if (mainCounter == 0){
		if (val.compare("function") != 0){
			msg = "error: the file must start with \"function main\"\n";
			mainCounter = 2;
		}
	}
	else if (mainCounter == 1){
		if (val.compare("main") != 0){
			msg = "error: the file must start with \"function main\"\n";
			mainCounter = 2;
		}
	}

	mainCounter++;
	return msg;

}